/**
 * 将日期格式化为： YYYY-MM-DD HH:FF:SS.SSS。 MM最小为1，表示1月。
 * @param date - Date
 * @returns string 格式为 'YYYY-MM-DD HH:FF:SS.SSS'。
 */
export function formatDate(date: Date = new Date(), format = `YYYY-MM-DD HH:FF:SS.SSS`): string {
	const result = format.replace('YYYY', date.getFullYear() + '')
		.replace('MM', (date.getMonth() + 1).toString().padStart(2, '0'))// 月份从0开始
		.replace('DD', date.getDate().toString().padStart(2, '0'))
		.replace('HH', date.getHours().toString().padStart(2, '0'))
		.replace('FF', date.getMinutes().toString().padStart(2, '0'))
		.replace('SSS', date.getMilliseconds().toString().padStart(3, '0'))
		.replace('SS', date.getSeconds().toString().padStart(2, '0'));
	return result;
}

/**
 * 将日期格式化为： YYYY-MM-DD。 MM最小为1，表示1月。
 * @param {Date} date 
 * @return string 格式为 'YYYY-MM-DD'。
 */
export function getDate(date: Date = new Date()): string {
	const format = `YYYY-MM-DD`;
	const result = format.replace('YYYY', date.getFullYear() + '')
		.replace('MM', (date.getMonth() + 1).toString().padStart(2, '0'))// 月份从0开始
		.replace('DD', date.getDate().toString().padStart(2, '0'))
		.replace('HH', date.getHours().toString().padStart(2, '0'))
	return result;
}

/**
 * 获取日期的时间戳，已经将其中的时间部分全部设置为0了。
 * @param {Date} date 
 * @return number
 */
export function getDateMs(date: Date = new Date()): number {
	const temp = new Date(date.getTime());
	temp.setHours(0);
	temp.setMinutes(0);
	temp.setSeconds(0);
	temp.setMilliseconds(0);
	return temp.getTime();
}

/**
 * 获取时间的时间戳（相对于0点的毫秒数），已经将其中的日期部分全部设置为0了。
 * @param {Date} date 
 * @return number
 */
export function getTimeMs(date: Date = new Date()): number {
	return (date.getHours() * 3600000 + date.getMinutes() * 60000 + date.getSeconds() * 1000 + date.getMilliseconds());
}

/**
 * 仅获取不带有毫秒的时间。
 * @param {Date} date 
 * @return string 格式为 'HH:FF:SS'。
 */
export function getTime(date: Date = new Date()): string {
	const format = `HH:FF:SS`;
	const result = format.replace('HH', date.getHours().toString().padStart(2, '0'))
		.replace('FF', date.getMinutes().toString().padStart(2, '0'))
		.replace('SS', date.getSeconds().toString().padStart(2, '0'));
	return result;
}

/**
 * 复制一个新的日期，跟指定日期相关若干天。
 * @param {Date} date 
 * @param {number} disDay 
 * @return 
 */
export function daysSkip(date: Date = new Date(), disDay: number = 0): Date {
	const newDate = new Date(date.getTime());
	newDate.setDate(newDate.getDate() + disDay);
	return newDate;
}

/**
 * 仅获取带有毫秒的时间。
 * @param {Date} date 
 * @return string 格式为 'HH:FF:SS.SSS'。
 */
export function getTimes(date: Date = new Date()): string {
	const format = `HH:FF:SS.SSS`;
	const result = format.replace('HH', date.getHours().toString().padStart(2, '0'))
		.replace('FF', date.getMinutes().toString().padStart(2, '0'))
		.replace('SSS', date.getMilliseconds().toString().padStart(3, '0'))
		.replace('SS', date.getSeconds().toString().padStart(2, '0'));
	return result;
}

/**
 * 复制一个日期对象。
 * @param {Date} date 
 * @return Date
 */
export function cloneDate(date: Date = new Date()) {
	return new Date(date.getTime());
}

/**
 * 去掉 'YYYY-MM-DD HH:FF:SS.SSS' 中的毫秒部分，只保留日期和时间。
 * @param date -  'YYYY-MM-DD HH:FF:SS.SSS' 或者  'YYYY-MM-DD HH:FF:SS'
 * @returns  'YYYY-MM-DD HH:FF:SS'
 */
export function getDateTimeFromDateStr(date: string) {
	return date.replace(/\.\d{3}$/, '');
}

/**
 * 只保留日期。
 * @param date -  'YYYY-MM-DD HH:FF:SS.SSS' 或者  'YYYY-MM-DD HH:FF:SS'
 * @returns  'YYYY-MM-DD'
 */
export function getDateFromDateStr(date: string) {
	return date.slice(0, 10);
}

/**
 * 只保留时间。
 * @param date -  'YYYY-MM-DD HH:FF:SS.SSS' 或者  'YYYY-MM-DD HH:FF:SS'
 * @returns  'HH:FF:SS'
 */
export function getTimeFromDateStr(date: string) {
	return date.slice(11, 19);
}

/**
 * 将日期： YYYY-MM-DD HH:FF:SS.SSS 转换为日期对象。 MM最小为1。
 * @param format - string
 * @returns Date
 */
export function getDateFromStr(format: string) {
	const arr = format.trim().split(/[-\s:\.]+/g);
	if (arr.length < 3) {
		throw new Error(`日期格式有误`);
	}
	const result = new Date();
	if (arr[0]) {
		result.setFullYear(parseInt(arr[0], 10));
	}
	if (arr[1]) {
		result.setMonth(parseInt(arr[1], 10) - 1);
	}
	if (arr[2]) {
		result.setDate(parseInt(arr[2], 10));
	}

	if (arr[3]) {
		result.setHours(parseInt(arr[3], 10));
	}
	if (arr[4]) {
		result.setMinutes(parseInt(arr[4], 10));
	}
	if (arr[5]) {
		result.setSeconds(parseInt(arr[5], 10));
	}
	if (arr[6]) {
		result.setMilliseconds(parseInt(arr[6], 10));
	}

	return result;
}

/**
 * 根据经度计算当地时间（当地时间的12点一定是当天太阳最高的时候）
 * @param longitude 经度，[-180,180]，东经为正，西经为负
 * @param beijinDate 北京时间。假设传入的北京时间是正确的。而且不会使用date中的时区信息，直接用8小时写死的（防止当前系统不是位于东8区）
 * @returns Date  这个日期对象中的 “年-月-日 时:分:秒” 就是当地时间。
 */
export function getLocalSolarTime8H(longitude: number, beijinDate: Date = new Date()): Date {
	if (longitude === 1001) { // 经度不正确
		return beijinDate;
	}
	// 直接计算相对于北京时间的偏移来计算时间
	let dis = 0;
	if (longitude > 120) {
		dis = longitude - 120;
	} else if (longitude > 0) {
		dis = -(120 - longitude);
	} else { // longitude <= 0
		dis = -120 + longitude;
	}
	// 计算太阳时偏移（经度每15度对应1小时）
	const totalHours = dis / 15;
	// 分解为小时、分钟、秒
	const [hours, minutes, seconds] = hourToHFS(Math.abs(totalHours));
	const sign = Math.sign(totalHours);

	return new Date(
		beijinDate.getFullYear(), beijinDate.getMonth(), beijinDate.getDate(),
		beijinDate.getHours() + sign * hours, beijinDate.getMinutes() + sign * minutes, beijinDate.getSeconds() + sign * seconds,
		beijinDate.getMilliseconds()
	);
}

export function getLocalZone(longitude: number): string {
	if (longitude === 1001) { // 经度不正确
		return '';
	}
	const totalHours = longitude / 15;
	const sign = totalHours < 0 ? '-' : '+';
	const preZero = Math.abs(totalHours) < 10 ? '0' : '';
	return sign + preZero + Math.abs(totalHours).toFixed(2);
}



/**
 * 根据经度计算当地时间（当地时间的12点一定是当天太阳最高的时候）
 * @param longitude 经度，[-180,180]，东经为正，西经为负
 * @param beijinDate 北京时间。假设当前系统位于东8区且传入的北京时间是正确的。
 * @returns Date  这个日期对象中的 “年-月-日 时:分:秒” 就是当地时间。
 */
export function getLocalSolarTime(longitude: number, beijinDate: Date = new Date()): Date {
	if (longitude === 1001) { // 经度不正确
		return beijinDate;
	}
	// 获取UTC时间的总小时数（含小数部分）
	const utcTotalHours = beijinDate.getUTCHours() + beijinDate.getUTCMinutes() / 60 + beijinDate.getUTCSeconds() / 3600;

	// 计算太阳时偏移（经度每15度对应1小时）
	const solarOffsetHours = longitude / 15;
	const solarTimeTotalHours = utcTotalHours + solarOffsetHours;

	// 相差的天数
	const disDate = solarTimeTotalHours >= 24 ? 1 : solarTimeTotalHours <= -24 ? -1 : 0;

	// 确保时间在0-24范围内
	const totalHours = ((solarTimeTotalHours % 24) + 24) % 24; // -25%24结果为-1

	// 分解为小时、分钟、秒
	const [hours, minutes, seconds] = hourToHFS(totalHours);

	return new Date(
		beijinDate.getUTCFullYear(), beijinDate.getUTCMonth(), beijinDate.getUTCDate() + disDate,
		hours, minutes, seconds, beijinDate.getMilliseconds()
	);
}

/**
 * 分解为小时、分钟、秒
 * @param totalHours 小时数，如 13.783h，相当于 13:45:32
 * @returns [小时、分钟、秒]
 */
function hourToHFS(totalHours: number) {
	const hours = Math.floor(totalHours);
	const minutes = Math.floor((totalHours - hours) * 60);
	const seconds = Math.floor(((totalHours - hours) * 60 - minutes) * 60);
	return [hours, minutes, seconds];
}


/**
 * 从网络获取当前的北京时间。（因为本地系统的时间可能会被修改）
 * 时间不是特别准确，应该能精确到1s内。毫秒部分就使用用户系统的毫秒（因为用户一般修改不了毫秒部分）
 * @returns Promise<Date> 注意，返回的日期对象中的时区是当前系统的时区（可能不是北京的东8区）。但日期和时间部分的数值是正确的北京时间。
 */
export async function getBeiJinTimeFromNet() {
	let date: Date | null = null;
	if (date === null) {
		try {
			date = await getBeiJinTimeFrom1().catch(e => null);
		} catch (e) {

		}

	}
	if (date === null) {
		try {
			date = await getBeiJinTimeFrom2().catch(e => null);
		} catch (e) {

		}
	}
	if (date === null) {
		try {
			date = await getBeiJinTimeFrom3().catch(e => null);
		} catch (e) {

		}
	}
	return date as Date;
}

/**
 * 从网络获取当前的北京时间。（因为本地系统的时间可能会被修改）
 * 时间不是特别准确，应该能精确到1s内。毫秒部分就使用用户系统的毫秒（因为用户一般修改不了毫秒部分）
 * @returns Promise<Date>  
 */
function getBeiJinTimeFrom1(): Promise<Date> {
	/**
	 * 返回的data = {
		"utc_offset": "+00:00",
		"timezone": "Etc/UTC",
		"day_of_week": 3,
		"day_of_year": 43,
		"datetime": "2025-02-12T07:44:44.650926+00:00",
		"utc_datetime": "2025-02-12T07:44:44.650926+00:00",
		"unixtime": 1739346284,
		"raw_offset": 0,
		"week_number": 7,
		"dst": false,
		"abbreviation": "UTC",
		"dst_offset": 0,
		"dst_from": null,
		"dst_until": null,
		"client_ip": "182.149.202.238"
	}
	 */
	return new Promise((resolve, reject) => {
		uni.request({
			url: 'https://worldtimeapi.org/api/timezone/Etc/UTC',
			success: (res) => {
				const data: any = res.data;
				console.log('worldtimeapi=', data);
				const date = createDateNoZone(data.datetime, 8.0);
				resolve(date);
			},
			fail: (e) => {
				console.error('request worldtimeapi error:', e);
				reject(e);
			}
		});
	});
}


function getBeiJinTimeFrom2(): Promise<Date> {
	/**
	 * 返回的 data = {
			"year": 2025,
			"month": 2,
			"day": 12,
			"hour": 9,
			"minute": 43,
			"seconds": 31,
			"milliSeconds": 506,
			"dateTime": "2025-02-12T09:43:31.5068298",
			"date": "02/12/2025",
			"time": "09:43",
			"timeZone": "Etc/UTC",
			"dayOfWeek": "Wednesday",
			"dstActive": false
		}
	 */
	return new Promise((resolve, reject) => {
		uni.request({
			url: 'https://timeapi.io/api/Time/current/zone?timeZone=Etc/UTC',
			success: (res) => {
				const data: any = res.data;
				console.log('timeapi=', data);
				const date = createDateNoZone(data.dateTime, 8.0);
				resolve(date);
			},
			fail: (e) => {
				console.error('request timeapi error:', e);
				reject(e);
			}
		});
	});
}


// 苏宁易购的api https://quan.suning.com/getSysTime.do
function getBeiJinTimeFrom3(): Promise<Date> {
	/**
	 * 返回的 data = {"sysTime2":"2025-02-12 16:30:56", // 就是当前的北京时间，也有可能是已经转换为了当前系统的时区中的时间（究竟是哪种情况暂时无法确认）
	 * "sysTime1":"20250212163056"} // 当前北京时间的另一种格式
	 */
	return new Promise((resolve, reject) => {
		uni.request({
			url: 'https://quan.suning.com/getSysTime.do',
			success: (res) => {
				const data: any = res.data;
				console.log('suning=', data);
				const date = createDateNoZone(data.sysTime2, 0.0);
				resolve(date);
			},
			fail: (e) => {
				console.error('request suning error:', e);
				reject(e);
			}
		});
	});
}


/**
 * 识别字符串中的时间，但不识别其中的时区
 * @param timeStr '2025-02-12T07:44:44.650926'  
 * @param offset 时区单独传入。
 * @returns 
 */
function createDateNoZone(timeStr: string, offset = 0) {
	const [y, m, d, h, f, s] = timeStr.split(/[-T:./ ]/).map(v => +v);
	const ms = +String(Date.now()).slice(-3); // 当前系统的毫秒
	// 因为如果直接传入一个字符串并且里面有时区的话，就会识别其中的时区并自动转换成当前系统所在时区的时间。
	// 所以，这里直接传入具体
	return new Date(y, m, d, h + offset, f, s, ms);
}

